-- {{{ Required libraries
local awesome, client, mouse, screen, tag = awesome, client, mouse, screen, tag
local ipairs, string, os, table, tostring, tonumber, type = ipairs, string, os,
                                                            table, tostring,
                                                            tonumber, type

-- Standard awesome library
local gears = require("gears") -- Utilities such as color parsing and objects
local awful = require("awful") -- Everything related to window managment
require("awful.autofocus")

-- Widget and layout library
local wibox = require("wibox")

-- Theme handling library
local beautiful = require("beautiful")

-- Notification library
local naughty = require("naughty")

-- Enable hotkeys help widget for VIM and other apps
-- when client with a matching name is opened:
local hotkeys_popup = require("awful.hotkeys_popup")
require("awful.hotkeys_popup.keys")

local my_table = awful.util.table or gears.table

-- Color vars
local c_bg = '#2f4452'
local c_fg = '#cbd0ec'
local c_ter = '#617698'
local c_sec = '#31375a'
local c_pri = '#5c4e90'

-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
    naughty.notify({
        preset = naughty.config.presets.critical,
        title = "Oops, there were errors during startup!",
        text = awesome.startup_errors
    })
end

-- Handle runtime errors after startup
do
    local in_error = false
    awesome.connect_signal("debug::error", function(err)
        if in_error then return end
        in_error = true

        naughty.notify({
            preset = naughty.config.presets.critical,
            title = "Oops, an error happened!",
            text = tostring(err)
        })
        in_error = false
    end)
end

-- {{{ Autostart windowless processes
local function run_once(cmd_arr)
    for _, cmd in ipairs(cmd_arr) do
        awful.spawn.with_shell(string.format(
                                   "pgrep -u $USER -fx '%s' > /dev/null || (%s)",
                                   cmd, cmd))
    end
end

run_once({"unclutter -root"}) -- entries must be comma-separated

local chosen_theme = ""
beautiful.init(
    string.format(
        "%s/.config/awesome/themes/%s/theme.lua",
        os.getenv("HOME"), chosen_theme
    ))

-- modkey or mod4 = super key
local modkey = "Mod4"
local altkey = "Mod1"
local ctrlKey = "Control"

-- personal variables
-- change these variables if you want
local browser1 = "librewolf"
local browser2 = "firefox"
local browser3 = "brave"
local editor = os.getenv("EDITOR") or "nano"
local editorgui = "codium"
local filemanager = "pcmanfm"
local mailclient = "evolution"
local mediaplayer = "mpv"
local terminal = "alacritty"
local virtualmachine = "virtualbox"

-- awesome variables
awful.util.terminal = terminal
awful.util.tagnames = {
    "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"
}

awful.layout.suit.tile.left.mirror = true
awful.layout.layouts = {
    awful.layout.suit.tile, awful.layout.suit.floating,
    awful.layout.suit.tile.left, awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top, 
    awful.layout.suit.max,
    awful.layout.suit.magnifier
}

-- {{{ Screen
-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal("property::geometry", function(s)
    -- Wallpaper
    if beautiful.wallpaper then
        local wallpaper = beautiful.wallpaper
        -- If wallpaper is a function, call it with the screen
        if type(wallpaper) == "function" then wallpaper = wallpaper(s) end
        gears.wallpaper.maximized(wallpaper, s, true)
    end
end)

-- No borders when rearranging only 1 non-floating or maximized client
screen.connect_signal("arrange", function(s)
    local only_one = #s.tiled_clients == 1
    for _, c in pairs(s.clients) do
        if only_one and not c.floating or c.maximized then
            c.border_width = 2
        else
            c.border_width = beautiful.border_width
        end
    end
end)

-- Create a wibox for each screen and add it
awful.screen.connect_for_each_screen(
    function(s)
        beautiful.at_screen_connect(s)
        s.systray = wibox.widget.systray()
        s.systray.visible = true
    end
)

globalkeys = my_table.join(
    -- HOTKEYS

    awful.key({modkey}, "w",
        function()
            awful.util.spawn(browser1)
        end,
        {description = browser1, group = "hotkeys"}),

    awful.key({modkey}, "r", 
        function()
            awful.spawn(
                string.format(
                    "dmenu_run -i -l 8 -nb '%s' -nf '%s' -sb '%s' -sf '%s' -fn NotoMonoRegular:bold:pixelsize=14",
                    c_bg, c_fg, c_pri, c_bg))
        end, 
        {description = "show dmenu", group = "hotkeys"}),

    awful.key({modkey}, "x",
        function() 
            awful.util.spawn("suclos") 
        end,
        {description = "exit", group = "hotkeys"}),

    awful.key({modkey}, "p",
        function() 
            awful.spawn.with_shell("$HOME/screenshotutil.sh")
        end,
        {description = "Screenshot", group = "hotkeys"}),

    -- SUPER

    awful.key({modkey}, "v", 
        function()
            awful.util.spawn("pavucontrol")
        end, 
        {description = "pulseaudio control", group = "super"}),

    awful.key({modkey}, "Return", 
        function() 
            awful.spawn(terminal) 
        end,
        {description = terminal, group = "super"}),

    awful.key({modkey}, "e", 
        function()
            awful.util.spawn(filemanager)
        end, 
        {description = filemanager, group = "super"}),

    -- ALT + CTRL

    awful.key({ctrlKey, altkey}, "f",
        function()
            awful.util.spawn(browser2)
        end,
        {description = browser2, group = "alt+ctrl"}),

    awful.key({ctrlKey, altkey}, "g",
        function() 
            awful.util.spawn(browser3) 
        end,
        {description = browser3,group = "alt+ctrl"}),

    awful.key({ctrlKey, altkey}, "o",
        function()
            awful.spawn.with_shell("$HOME/.config/awesome/scripts/picom-toggle.sh")
        end, 
        {description = "Picom toggle", group = "alt+ctrl"}),

    -- ALTKEY
                           
    awful.key({altkey}, "Left", 
        function()
            awful.util.spawn("variety -p")
        end, 
        {description = "Wallpaper previous", group = "altkey"}),
                           
    awful.key({altkey}, "Right", 
        function()
            awful.util.spawn("variety -n")
        end, 
        {description = "Wallpaper next", group = "altkey"}),
                           
    awful.key({altkey}, "Up", 
        function()
            awful.util.spawn("variety --pause")
        end, 
        {description = "Wallpaper pause", group = "altkey"}),
                           
    awful.key({altkey}, "Down", 
        function()
            awful.util.spawn("variety --resume")
        end, 
        {description = "Wallpaper resume", group = "altkey"}),

    -- AWESOME

    awful.key({modkey}, "s", 
        hotkeys_popup.show_help, 
        {description = "show help",group = "awesome"}),

    awful.key({modkey}, "b", 
        function()
            for s in screen do
                s.mywibox.visible = not s.mywibox.visible
                if s.mybottomwibox then
                    s.mybottomwibox.visible = not s.mybottomwibox.visible
                end
            end
        end, 
        {description = "toggle wibox", group = "awesome"}),
        
    awful.key({modkey, "Shift"}, "r", 
        awesome.restart, 
        {description = "reload awesome",group = "awesome"}), 

    awful.key({altkey}, "x", 
        function()
            awful.prompt.run {
                prompt = "Run Lua code: ",
                textbox = awful.screen.focused().mypromptbox.widget,
                exe_callback = awful.util.eval,
                history_path = awful.util.get_cache_dir() .. "/history_eval"
            }
        end, 
        {description = "lua execute prompt", group = "awesome"}),
        
    -- TAG

    awful.key({modkey}, "Left", 
        awful.tag.viewprev,
        {description = "view previous", group = "tag"}),

    awful.key({modkey}, "Right", 
        awful.tag.viewnext,
        {description = "view next", group = "tag"}),

    awful.key({altkey}, "Tab", 
        awful.tag.viewnext,
        {description = "view next", group = "tag"}),
                           
    awful.key({altkey, "Shift"}, "Tab",
        awful.tag.viewprev, 
        {description = "view previous",group = "tag"}),

    -- CLIENT

    awful.key({altkey}, "j",
        function ()
            awful.client.focus.byidx( 1)
        end,
        {description = "focus next by index", group = "client"}),

    awful.key({modkey}, "j", 
        function()
            awful.client.focus.global_bydirection("down")
            if client.focus then 
                client.focus:raise() 
            end
        end, 
        {description = "focus down", group = "client"}),
        
    awful.key({modkey}, "k", 
        function()
            awful.client.focus.global_bydirection("up")
            if client.focus then 
                client.focus:raise() 
            end
        end, 
        {description = "focus up", group = "client"}),
        
    awful.key({modkey}, "h", 
        function()
            awful.client.focus.global_bydirection("left")
            if client.focus then 
                client.focus:raise() 
            end
        end, 
        {description = "focus left", group = "client"}),
                           
    awful.key({modkey}, "l", 
        function()
            awful.client.focus.global_bydirection("right")
            if client.focus then 
                client.focus:raise() 
            end
        end, 
        {description = "focus right", group = "client"}),

    awful.key({modkey, "Shift"}, "j", 
        function() 
            awful.client.swap.byidx(1) 
        end,
        {description = "swap with next client by index", group = "client"}),
        
    awful.key({modkey, "Shift"}, "k", 
        function()
            awful.client.swap.byidx(-1)
        end, 
        {description = "swap with previous client by index", group = "client"}),
        
    awful.key({modkey}, "u", 
        awful.client.urgent.jumpto,
        {description = "jump to urgent client",group = "client"}), 

    awful.key({ctrlKey}, "Tab", 
        function()
            awful.client.focus.history.previous()
            if client.focus then 
                client.focus:raise() 
            end
        end, 
        {description = "go back", group = "client"}),

    awful.key({modkey, "Control"}, "n", 
        function()
            local c = awful.client.restore()
            if c then
                client.focus = c
                c:raise()
            end
        end, 
        {description = "restore minimized", group = "client"}),

    -- LAYOUT

    awful.key({altkey, "Shift"}, "l", 
        function() 
            awful.tag.incmwfact(0.05) 
        end,
        {description = "increase master width factor", group = "layout"}),
        
    awful.key({altkey, "Shift"}, "h",
        function() 
            awful.tag.incmwfact(-0.05) 
        end,
        {description = "decrease master width factor",group = "layout"}),

    awful.key({modkey, "Shift"}, "h",
        function() 
            awful.tag.incnmaster(1, nil, true) 
        end, 
        {description = "increase the number of master clients",group = "layout"}),

    awful.key({modkey, "Shift"}, "l",
        function() 
            awful.tag.incnmaster(-1, nil, true) 
        end, 
        {description = "decrease the number of master clients",group = "layout"}), 

    awful.key({modkey, "Control"}, "h",
        function() 
            awful.tag.incncol(1, nil, true) 
        end, 
        {description = "increase the number of columns",group = "layout"}),

    awful.key({modkey, "Control"}, "l",
        function() 
            awful.tag.incncol(-1, nil, true) 
        end, 
        {description = "decrease the number of columns",group = "layout"}),

    awful.key({modkey}, "space", 
        function() 
            awful.layout.inc(1) 
        end,
        {description = "select next", group = "layout"}),

    -- VOLUME
    
    awful.key({ctrlKey, "Shift"}, "=", 
        function()
            os.execute(string.format("amixer -q set %s 5%%+", beautiful.volume.channel))
            beautiful.volume.update()
        end, 
        {description = "Raise volume", group = "Volume"}),

    awful.key({ctrlKey, "Shift"}, "-", 
        function()
            os.execute(string.format("amixer -q set %s 5%%-", beautiful.volume.channel))
            beautiful.volume.update()
        end, 
        {description = "Lower volume", group = "Volume"}),
        
    awful.key({ctrlKey, "Shift"}, "m", 
        function()
            os.execute(
                string.format(
                    "amixer -q set %s toggle", 
                    beautiful.volume.togglechannel or beautiful.volume.channel))
            beautiful.volume.update()
        end, 
        {description = "Mute", group = "Volume"}),


    awful.key({ctrlKey, "Shift"}, "p", 
        function()
            os.execute("pacmd set-default-sink 0")	
        end,
        {description = "Sink to speakers", group = "Volume"}),

    awful.key({ctrlKey, "Shift"}, "h", 
        function()
            os.execute("pacmd set-default-sink 1")	
        end,
        {description = "Sink to headset", group = "Volume"})
)

clientkeys = my_table.join(
    -- HOTKEYS

    awful.key({modkey, "Shift"}, "q",
        function(c) 
            c:kill() 
        end,
        {description = "close", group = "hotkeys"}),

    -- CLIENT

    awful.key({modkey}, "f", 
        function(c)
            c.fullscreen = not c.fullscreen
            c:raise()
        end, 
        {description = "toggle fullscreen", group = "client"}),
                           
    awful.key({modkey, "Shift"}, "space",
        awful.client.floating.toggle, 
        {description = "toggle floating",group = "client"}), 

    awful.key({modkey}, "n", 
        function(c)
            c.minimized = true
        end, 
        {description = "minimize", group = "client"}),
                           
    awful.key({modkey}, "m", 
        function(c)
            c.maximized = not c.maximized
            c:raise()
        end, 
        {description = "maximize", group = "client"})
)

-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it works on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
    -- Hack to only show tags 1 and 9 in the shortcut window (mod+s)
    local descr_view, descr_toggle, descr_move, descr_toggle_focus
    if i == 1 or i == 9 then
        descr_view = {description = "view tag #", group = "tag"}
        descr_toggle = {description = "toggle tag #", group = "tag"}
        descr_move = {
            description = "move focused client to tag #",
            group = "tag"
        }
        descr_toggle_focus = {
            description = "toggle focused client on tag #",
            group = "tag"
        }
    end

    globalkeys = my_table.join(globalkeys,
        awful.key({modkey}, "#" .. i + 9, 
            function()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then 
                    tag:view_only() 
                end
            end, 
            descr_view),

        awful.key({modkey, "Control"}, "#" .. i + 9, 
            function()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then 
                    awful.tag.viewtoggle(tag) 
                end
            end, 
            descr_toggle),

        awful.key({modkey, "Shift"}, "#" .. i + 9, 
            function()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then
                        client.focus:move_to_tag(tag)
                        tag:view_only()
                    end
                end
            end, 
            descr_move),

        awful.key({modkey, "Control", "Shift"}, "#" .. i + 9, 
            function()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then 
                        client.focus:toggle_tag(tag) 
                    end
                end
            end, 
            descr_toggle_focus)
        )
end

clientbuttons = gears.table.join(
        awful.button({}, 1, 
            function(c)
                c:emit_signal("request::activate", "mouse_click", {raise = true})
            end), 
            
        awful.button({modkey}, 1, 
            function(c)
                c:emit_signal("request::activate", "mouse_click", {raise = true})
                awful.mouse.client.move(c)
            end), 
            
        awful.button({modkey}, 3, 
            function(c)
                c:emit_signal("request::activate", "mouse_click", {raise = true})
                awful.mouse.client.resize(c)
            end)
        )

-- Set keys
root.keys(globalkeys)
-- }}}

-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
    -- All clients will match this rule.
    {
        rule = {},
        properties = {
            border_width = beautiful.border_width,
            border_color = beautiful.border_normal,
            focus = awful.client.focus.filter,
            raise = true,
            keys = clientkeys,
            buttons = clientbuttons,
            screen = awful.screen.preferred,
            placement = awful.placement.no_overlap +
                awful.placement.no_offscreen,
            size_hints_honor = false
        }
    },
    {
        rule_any = {type = {"dialog", "normal"}},
        properties = {titlebars_enabled = false}
    }, 
    {rule = {class = editorgui}, properties = {maximized = true}},
}


-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal("manage", function(c)
    -- Set the windows at the slave,
    -- i.e. put it at the end of others instead of setting it master.
    -- if not awesome.startup then awful.client.setslave(c) end

    if awesome.startup and not c.size_hints.user_position and
        not c.size_hints.program_position then
        -- Prevent clients from being unreachable after screen count changes.
        awful.placement.no_offscreen(c)
    end
end)

-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
    -- Custom
    if beautiful.titlebar_fun then
        beautiful.titlebar_fun(c)
        return
    end

    -- Default
    -- buttons for the titlebar
    local buttons = my_table.join(awful.button({}, 1, function()
        c:emit_signal("request::activate", "titlebar", {raise = true})
        awful.mouse.client.move(c)
    end), awful.button({}, 3, function()
        c:emit_signal("request::activate", "titlebar", {raise = true})
        awful.mouse.client.resize(c)
    end))

    awful.titlebar(c, {size = dpi(21)}):setup{
        { -- Left
            awful.titlebar.widget.iconwidget(c),
            buttons = buttons,
            layout = wibox.layout.fixed.horizontal
        },
        { -- Middle
            { -- Title
                align = "center",
                widget = awful.titlebar.widget.titlewidget(c)
            },
            buttons = buttons,
            layout = wibox.layout.flex.horizontal
        },
        { -- Right
            awful.titlebar.widget.floatingbutton(c),
            awful.titlebar.widget.maximizedbutton(c),
            awful.titlebar.widget.stickybutton(c),
            awful.titlebar.widget.ontopbutton(c),
            awful.titlebar.widget.closebutton(c),
            layout = wibox.layout.fixed.horizontal()
        },
        layout = wibox.layout.align.horizontal
    }
end)

-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
    c:emit_signal("request::activate", "mouse_enter", {raise = false})
end)

client.connect_signal("focus",
                      function(c) c.border_color = beautiful.border_focus end)
client.connect_signal("unfocus",
                      function(c) c.border_color = beautiful.border_normal end)

-- }}}

-- Autostart applications
awful.spawn.with_shell("~/.config/awesome/autostart.sh")
awful.spawn.with_shell("picom -b --config  $HOME/.config/awesome/picom.conf")
